package com.sise.cn.dao.base;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sise.cn.constant.CommonConstant;
import com.sise.cn.dao.pagehelper.PageBean;
import com.sise.cn.enums.error.SystemError;
import com.sise.cn.exception.SystemException;
import com.sise.cn.utils.Maps;
import com.sise.cn.utils.Pair;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @ClassName BaseMapper
 * @Description
 * @Author CCJ
 * @Date 2020/6/2 21:57
 **/
@Slf4j
@Component
public class BaseMapperImpl extends BaseMapper {

    private static final String PARAMS = "__params__";

    private static final String ORDER_BY = "__orderBy__";

    private static final String GROUP_BY = "__groupBy__";

    private static final String LIMIT = "__limit__";

    private static final String PAGE = "__page__";

    private static final String START_TIME = "__startTime__";

    private static final String END_TIME = "__endTime__";

    private static final String COLUMNS = "__columns__";

    private static final String UPDATE_LIST = "__updateList__";

    private static final String LIST = "__list__";

    private static final String FIELDS = "__fields__";

    @Override
    public <T> T select(TableSpace condition, Class<? extends TableSpace> t) {
        Map<String, Object> map = new HashMap<>();
        prefixSelect(condition, map);
        JSONObject json = sqlSessionTemplate.selectOne(BASE_MAPPER + SELECT, map);
        if (json == null) {
            return null;
        }
        return (T) JSON.parseObject(json.toJSONString(), t);
    }

    private void prefixSelect(TableSpace condition, Map<String, Object> map) {
        map.put(DB, getDb());
        map.put(TABLE, mapperName(condition.getClass()));
        map.put(COLUMNS, showColumns(condition.getClass()));
        map.put(PARAMS, condition.invokeParams());
        map.put(ORDER_BY, condition.get__orderBy__());
        map.put(GROUP_BY, condition.get__groupBy__());
        map.put(START_TIME, condition.get__startTime__());
        map.put(END_TIME, condition.get__endTime__());
        if (getSoftDelete()) {
            ((List<Map<String, Object>>) map.get(PARAMS)).add(Maps.ofMap("deleted", CommonConstant.ON));
        }
    }


    private void startPage(TableSpace condition, Map<String, Object> map) {
        map.put(LIMIT, condition.get__limit__());
        map.put(PAGE, condition.get__page__());
    }


    private void prefixUpdate(TableSpace tableSpace, Map<String, Object> map) {
        Pair<Object, Object> pair = tableSpace.invokeUpdate();
        map.put(DB, getDb());
        map.put(TABLE, mapperName(tableSpace.getClass()));
        map.put(PARAMS, pair.getFirst());
        map.put(UPDATE_LIST, pair.getSecond());
    }

    private Map<String, Object> prefixSimpleUpdate(TableSpace tableSpace, Map<String, Object> whereMap) {
        Pair<Object, Object> pair = tableSpace.initUpdate(whereMap);
        Map<String, Object> map = new HashMap<>(4);
        map.put(DB, getDb());
        map.put(TABLE, mapperName(tableSpace.getClass()));
        map.put(PARAMS, pair.getFirst());
        map.put(UPDATE_LIST, pair.getSecond());
        return map;
    }


    @Override
    public <T> List<T> selectList(TableSpace condition, Class<? extends TableSpace> t) {
        Map<String, Object> map = new HashMap<>();
        prefixSelect(condition, map);
        log.debug("select db param={}", map);
        List<JSONObject> sources = sqlSessionTemplate.selectList(BASE_MAPPER + SELECT, map);
        List<T> list = new ArrayList<>();
        for (JSONObject json : sources) {
            if (json == null) {
                continue;
            }
            list.add((T) JSON.parseObject(json.toJSONString(), t));
        }
        return list;
    }

    @Override
    public <T> PageBean<T> selectByPage(TableSpace condition, Class<? extends TableSpace> t) {
        Map<String, Object> map = new HashMap<>();
        long count = count(condition);
        prefixSelect(condition, map);
        startPage(condition, map);
        List<T> list = selectList(condition, t);
        PageBean<T> pageBean = new PageBean<>();
        pageBean.setList(list);
        pageBean.setPage(condition.get__page__());
        pageBean.setTotal(count);
        pageBean.setSize(condition.get__limit__());
        pageBean.setPrePage(condition.get__page__() >= 1);
        pageBean.setNextPage(count > condition.get__page__() * condition.get__limit__());
        return pageBean;
    }

    @Override
    public int update(TableSpace tableSpace) {
        Map<String, Object> map = new HashMap<>();
        prefixUpdate(tableSpace, map);
        return sqlSessionTemplate.update(BASE_MAPPER + UPDATE, map);
    }

    @Override
    public int simpleUpdate(TableSpace tableSpace, Map<String, Object> whereMap) {
        return sqlSessionTemplate.update(BASE_MAPPER + UPDATE, prefixSimpleUpdate(tableSpace, whereMap));
    }

    @Override
    public int delete(TableSpace condition) {
        Map<String, Object> map = new HashMap<>();
        if (getSoftDelete()) {
            condition.setDeleted(CommonConstant.OFF);
            return insertUpdate(condition);
        }
        prefixSelect(condition, map);
        return sqlSessionTemplate.delete(BASE_MAPPER + DELETE, map);
    }

    @Override
    public int insert(TableSpace tableSpace) {
        return batchInsert(Collections.singletonList(tableSpace));
    }

    @Override
    public Long insertAndGetId(TableSpace data) {
        if (data == null) {
            throw new SystemException(SystemError.OPEN_PARAM_ERROR, "data", "data=null");
        }
        Map<String, Object> map = new HashMap<>(16);
        List<Object> row = new ArrayList<>();
        List<Map<String, Object>> params = data.invokeParams();
        map.put(DB, getDb());
        map.put(TABLE, mapperName(data.getClass()));
        map.put(PARAMS, "");
        map.put(FIELDS, getInsertField(params));
        row.add(params);
        map.put(LIST, row);
        sqlSessionTemplate.insert(BASE_MAPPER + INSERT, map);
        log.info("this is insert to db param={}", map);
        return (Long) map.get("id");

    }

    @Override
    public int insertUpdate(TableSpace tableSpace) {
        return batchInsertUpdate(Collections.singletonList(tableSpace));
    }

    @Override
    public int batchInsertUpdate(List<TableSpace> list) {
        return insertOrUpdate(list, true);
    }

    @Override
    public int batchInsert(List<TableSpace> list) {
        return insertOrUpdate(list, false);
    }

    @Override
    public int count(TableSpace condition) {
        Map<String, Object> map = new HashMap<>();
        prefixSelect(condition, map);
        return sqlSessionTemplate.selectOne(BASE_MAPPER + COUNT, map);
    }

    @Override
    public List<String> showDatabase(String name) {
        Map<String, Object> map = new HashMap<>();
        map.put("databaseName", name);
        return sqlSessionTemplate.selectList(BASE_MAPPER+SHOW_DATABASES, map);
    }

    @Override
    public void executor(String sql) {
        Map<String, Object> map = new HashMap<>();
        map.put("sql", sql);
        sqlSessionTemplate.update(BASE_MAPPER+EXECUTOR, map);
    }

    private int insertOrUpdate(List<TableSpace> list, Boolean isUpdate) {
        if (CollectionUtils.isEmpty(list)) {
            throw new SystemException(SystemError.OPEN_PARAM_ERROR, "list", "list=" + list);
        }
        Map<String, Object> map = new HashMap<>();
        map.put(DB, getDb());
        List<Object> row = new ArrayList<>();
        boolean isFirst = true;
        for (TableSpace tableSpace : list) {
            if (tableSpace == null) {
                continue;
            }
            List<Map<String, Object>> params = tableSpace.invokeParams();
            if (isFirst) {
                map.put(TABLE, mapperName(tableSpace.getClass()));
                map.put(PARAMS, "");
                map.put(FIELDS, getInsertField(params));
                isFirst = false;
            }
            row.add(params);
        }
        map.put(LIST, row);
        if (isUpdate) {
            log.info("this is insert or update to db param={}", map);
            return sqlSessionTemplate.insert(BASE_MAPPER + INSERT_UPDATE, map);
        }
        log.info("this is insert to db param={}", map);
        return sqlSessionTemplate.insert(BASE_MAPPER + INSERT, map);
    }

    private List<String> getInsertField(List<Map<String, Object>> fields) {
        List<String> list = new ArrayList<>();
        for (Map<String, Object> entry : fields) {
            if (entry.get("value") != null) {
                list.add((String) entry.get("key"));
            }
        }
        return list;
    }
}
